#include "StdAfx.h"
#include "MultiscaleCrossModal.h"

//Constructor
CMultiscaleCrossModal::CMultiscaleCrossModal(_3dpoint size1, _2dpoint size2, unsigned int datadimension1,
											 unsigned int datadimension2, vector<double> maxvalue1, vector<double> minvalue1,
											 vector<double> maxvalue2,	vector<double> minvalue2, unsigned int traincycle)
{
	//Parameters
	maxcycle = traincycle;

	//Try to create the multiscale net, if not, throwing exception
	try
	{
		firstnet = new MultiscaleNet(size1.x, size1.y, size1.z, datadimension1, maxcycle, maxvalue1, minvalue1);
	}
	catch(string exceptionstring)
	{
		throw exceptionstring;
	}

	//Create the second single SOM
	unsigned int numberofdata;
	double* maxinput = bf.VectorToPointer(maxvalue2, numberofdata);
	if(numberofdata!=datadimension2)		//Varify the size of the data dimension
		throw string("Max value dimension not matched");
	double* mininput = bf.VectorToPointer(minvalue2, numberofdata);
	if(numberofdata!=datadimension2)		//Varify the size of the data dimension
		throw string("Min value dimension not matched");
	secondnet = new CSOM((int)size2.x, (int)size2.y, datadimension2, maxcycle, maxinput, mininput);
	delete[] mininput;
	delete[] maxinput;
	//Create the hebbian link
	hebbianlink = new CHebbian((int)(size1.x*size1.y), (int)(size2.x*size2.y));

	//Size of the map
	sizeoffirstnet.x = size1.x;
	sizeoffirstnet.y = size1.y;
	sizeofsecondnet = size2;
}

//Construct the network from files
CMultiscaleCrossModal::CMultiscaleCrossModal(string firstnetfile, string secondnetfile, string hebbianfile)
{
	try
	{
		firstnet = new MultiscaleNet(firstnetfile);
		secondnet = new CSOM(secondnetfile);
		hebbianlink = new CHebbian(hebbianfile);
		//Set the size of the map
		_3dpoint firstnetsize = firstnet->GetNetSize();
		sizeoffirstnet.x = firstnetsize.x;
		sizeoffirstnet.y = firstnetsize.y;
		sizeofsecondnet = secondnet->GetMapSize();
	}
	catch(string exceptionmessage)
	{
		throw exceptionmessage;
	}
}

CMultiscaleCrossModal::~CMultiscaleCrossModal(void)
{
	secondnet->somDestroy();
	hebbianlink->NetDestroy();
}

//Train the cross modal
void CMultiscaleCrossModal::NetTraining(std::vector<vector<vector<double> > > netinput1, std::vector<vector<double> > netinput2)
{
	//Varify the size of the two input
	if(netinput1.size()!=netinput2.size())
		throw string("Input size of two modals not match");

	//Convert the second input into pointer
	unsigned int input2size1, input2size2;
	double** secondinput;
	try
	{
		secondinput = bf.VectorToPointer(netinput2, input2size1, input2size2);
	}
	catch(string exceptionstring)
	{
		throw exceptionstring;
	}
	double** secondinputtranspose = bf.matrixTransp(secondinput, input2size2, input2size1);

	//Train the cross modal by cycles
	for(unsigned int i=0; i<maxcycle; i++)
	{
		//Train the first net
		vector<vector<vector<double>>> trainingweights;
		vector<_3dpoint> trainingwinners;
		try
		{
			firstnet->Training(netinput1, trainingwinners, trainingweights);
		}
		catch(string exceptionstring)
		{
			throw exceptionstring;
		}

		//Train the second net
		secondnet->somTraining(secondinputtranspose, input2size1);

		//Train the hebbian links
		//Get the distance and convert them into pointers
		vector<double> distance1 = firstnet->GetDistances();
		vector<double> distance2 = secondnet->somGetTrainingDistance();
		if(distance1.size()!=distance2.size())
			throw string("Distance size not matched");
		unsigned int number1, number2;
		double* trainingdistance1 = bf.VectorToPointer(distance1, number1);
		double* trainingdistance2 = bf.VectorToPointer(distance2, number2);
		//Get the training winners and convert into pointer
		int* trainingwinners2 = secondnet->getTrainingResults();
		int* trainingwinners1 = new int[trainingwinners.size()];
		for(unsigned int j=0; j<input2size1; j++)
		{
			trainingwinners1[j] = trainingwinners[j].y*sizeoffirstnet.x+trainingwinners[j].x;
			trainingdistance1[i] = pow(E, -trainingdistance1[i]);
			trainingdistance2[i] = pow(E, -trainingdistance2[i]);
		}
		hebbianlink->NetTraining(trainingdistance1, trainingdistance2, trainingwinners1, trainingwinners2, input2size1);

		//Delete the temporal memory
		delete[] trainingdistance1;
		delete[] trainingdistance2;
		delete[] trainingwinners1;
	}
	//Delete the training data
	for(unsigned int i=0; i<input2size1; i++)
		delete[] secondinput[i];
	delete[] secondinput;
	for(unsigned int i=0; i<input2size2; i++)
		delete[] secondinputtranspose[i];
	delete[] secondinputtranspose;

	//Create the hebian links
	hebbianlink->NetAssociated();

	return;
}

//Get the cross link for input from the first net
_2dpoint CMultiscaleCrossModal::GetCrossLink2(std::vector<vector<double> > input)
{
	//Get the winner from the first input
	_3dpoint testwinner = firstnet->FindWinner(input);
	int hebbian1 = (int)(testwinner.y*sizeoffirstnet.x+testwinner.x);
	//Get the cross link
	int hebbian2 = hebbianlink->NetFindOutput(hebbian1);
	_2dpoint secondwinner = {(unsigned int)(hebbian2%sizeofsecondnet.x), (unsigned int)(hebbian2/sizeofsecondnet.x)};
	return secondwinner;
}

//Get the cross link for input from the second net
_2dpoint CMultiscaleCrossModal::GetCrossLink1(std::vector<double> input)
{
	//Get the winner from the first input
	unsigned int numberofdata;
	double* secondinput = bf.VectorToPointer(input, numberofdata);
	int hebbian2 = secondnet->getTestingResults(secondinput);
	//Get the first winner
	int hebbian1 = hebbianlink->NetFindInput(hebbian2);
	_2dpoint firstwinner = {(unsigned int)(hebbian1%sizeoffirstnet.x), (unsigned int)(hebbian1/sizeoffirstnet.x)};
	delete[] secondinput;
	return firstwinner;
}

//Get the winner of the first net
_2dpoint CMultiscaleCrossModal::GetWinner1(std::vector<vector<double> > input)
{
	_3dpoint _3dwinner = firstnet->FindWinner(input);
	_2dpoint _2dwinner = {_3dwinner.x, _3dwinner.y};
	return _2dwinner;
}

//Get the winner of the second net
_2dpoint CMultiscaleCrossModal::GetWinner2(std::vector<double> input)
{
	unsigned int numberofdata;
	double* secondinput = bf.VectorToPointer(input, numberofdata);
	int _1dwinner = secondnet->getTestingResults(secondinput);
	_2dpoint _2dwinner = {(unsigned int)(_1dwinner%sizeofsecondnet.x), (unsigned int)(_1dwinner/sizeofsecondnet.y)};
	delete[] secondinput;
	return _2dwinner;
}

//Get the size of the first map
_2dpoint CMultiscaleCrossModal::GetMapSize1()
{
	return sizeoffirstnet;
}

//Get the size of the second map
_2dpoint CMultiscaleCrossModal::GetMapSize2()
{
	return sizeofsecondnet;
}

//Save the cross modal to files
void CMultiscaleCrossModal::SaveCrossModalToFile(std::string firstfile, std::string secondfile, std::string hebbianfile)
{
	firstnet->SaveNetIntoFile(firstfile);
	secondnet->somSaveWeights(secondfile);
	hebbianlink->NetSaveToFile(hebbianfile);
	return;
}

//Get Cross Modal links from net2
_2dpoint CMultiscaleCrossModal::GetCrossLink1(_2dpoint position2)
{
	int hebbian2 = position2.y*sizeofsecondnet.x+position2.x;
	int hebbian1 = hebbianlink->NetFindInput(hebbian2);
	//If no link is found
	if(hebbian1==-1)
	{
		_2dpoint firstwinner = {-1, -1};
		return firstwinner;
	}
	//else
	_2dpoint firstwinner = {(unsigned int)(hebbian1%sizeoffirstnet.x), (unsigned int)(hebbian1/sizeoffirstnet.x)};
	return firstwinner;
}

//Get Cross Modal links from net1
_2dpoint CMultiscaleCrossModal::GetCrossLink2(_2dpoint position1)
{
	int hebbian1 = position1.y*sizeoffirstnet.x+position1.x;
	int hebbian2 = hebbianlink->NetFindInput(hebbian1);
	//If no link is found
	if(hebbian2==-1)
	{
		_2dpoint secondwinner = {-1, -1};
		return secondwinner;
	}
	//else
	_2dpoint secondwinner = {(unsigned int)(hebbian2%sizeofsecondnet.x), (unsigned int)(hebbian2/sizeofsecondnet.x)};
	return secondwinner;
}
